
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2023/06/26
// Design Name:
// Module Name: iopmp_set_module
// Project Name: wujian100
// Target Devices:
// Tool Versions:
// Description:
//

// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// base ahb addr is 0x4001_0000  none offset
//////////////////////////////////////////////////////////////////////////////////
module iopmp_set_module (
    
    input                               iopmpst_clk_i        ,
    input                               iopmpst_rst_n_i      ,
    input [31:0]                        iopmpst_haddr_i      ,
    // input [2 :0]                        iopmpst_hburst_i     ,
    input [3 :0]                        iopmpst_hprot_i      ,
    input                               iopmpst_hsel_i       ,
    input [2 :0]                        iopmpst_hsize_i      ,
    input [1 :0]                        iopmpst_htrans_i     ,
    input [31:0]                        iopmpst_hwdata_i     ,
    input                               iopmpst_hwrite_i     ,

    input [31:0]                        iopmpst_readdata_i   ,
    // input                               iopmpst_hready_i     ,
    // input [1 :0]                        iopmpst_hresp_i      ,

    output reg [31:0]                   iopmpst_hrdata_o     ,
    output                              iopmpst_hready_o     ,
    output [1 :0]                       iopmpst_hresp_o      ,
    output                              iopmpst_intr_o       ,

    output                              iopmpst_node_clk_o   ,
    output                              iopmpst_node_rstn_o  ,
    output                              iopmpst_cfg_en       ,
    output [7:0]                        iopmpst_node_addr    ,
    output                              iopmpst_node_wen_o   ,
    output                              iopmpst_node_ren_o   ,
    output [31:0]                       iopmpst_node_wdata_o      

        
);
//interface fsm  ahb->reg
localparam IDLE        = 4'b0000;
localparam WRITECMD    = 4'b0001;
localparam WRITEDATA   = 4'b0010;
localparam READCMD     = 4'b0100;
localparam READDATA    = 4'b1000;

// localparam  CFGW   = 1'b0; 
// localparam  CFGOUT = 1'b1;

localparam  CONFIG_REG_OFFSET   = 3'd0;

localparam  SIOPMP_RULE_REG_OFFSET    = 3'd1;
// localparam  SIOPMP_SID_REG_OFFSET     = 3'd2;  // SOURCE ID，ree or dma
localparam  DIOPMP_BEGIN_REG_OFFSET   = 3'd2;
localparam  DIOPMP_SIZE_REG_OFFSET    = 3'd3;
localparam  DIOPMP_RULE_REG_OFFSET    = 3'd4;
localparam  DIOPMP_MODE_REG_OFFSET    = 3'd5;
localparam  SDIOPMP_ERROR_OFFSET      = 3'd6;

//localparam  CHECKILL_REG_OFFSET = 12'h004;

reg [31:0]  config_reg;

wire        write_en;
wire        read_en;
wire        config_reg_wen;
wire        config_reg_ren;
wire        en;
wire       write_data_en;
wire       read_data_en ;

wire [4:0]  iopmp_node_id;
reg [2:0]  addr_offset;
wire        hready;


// localparam UPDATERULE  = 5'b10000;
reg  [31:0]   write_data;
reg  [3:0]    state;
reg  [3:0]    state_next;


always @(posedge iopmpst_clk_i or negedge iopmpst_rst_n_i) begin
    if(!iopmpst_rst_n_i)
        state <= 4'd0;
    else
        state <= state_next;
end


always @(*) begin
    case (state)
        IDLE:
          begin
            if(write_en == 1'b1)
                state_next = WRITECMD;
            else if(read_en == 1'b1)  
                state_next = READCMD;
            else
                state_next = IDLE;

          end
        WRITECMD:
          begin
            if(read_en == 1'b1)
                state_next = READCMD;
            else if(write_en == 1'b1)
                state_next = WRITECMD;
            else if(hready == 1'b1)
                state_next = WRITEDATA;
            else
                state_next = WRITECMD;
          end
        READCMD:
          begin
            if(write_en == 1'b1)
                state_next = WRITECMD;
            else if(read_en == 1'b1)
                state_next = READCMD;
            else if(hready == 1'b1)
                state_next = READDATA;
            else
                state_next = READCMD;
          end
        WRITEDATA:
          begin
            if(write_en == 1'b1)
                state_next = WRITECMD;
            else if(read_en == 1'b1)  
                state_next = READCMD;
            else
                state_next = IDLE;
          end
        READDATA:
          begin 
            if(write_en == 1'b1)
                state_next = WRITECMD;
            else if(read_en == 1'b1)  
                state_next = READCMD;
            else
                state_next = IDLE;
          end
        default: state_next = IDLE;
    endcase
end
assign write_en = ((iopmpst_htrans_i[1] == 1) && (iopmpst_hwrite_i == 1) && (iopmpst_hsel_i == 1));
assign read_en  = ((iopmpst_htrans_i[1] == 1) && (iopmpst_hwrite_i == 0) && (iopmpst_hsel_i == 1));
assign en = write_en | read_en;

assign write_data_en = (state_next == WRITEDATA) || (state == WRITECMD && state_next == READCMD) || (state == WRITECMD && write_en == 1'b1);
assign read_data_en = (state_next == READDATA) || (state == READCMD && state_next == WRITECMD) || (state == READCMD && read_en == 1'b1);

always @(*) begin
    if(write_data_en)
       write_data = iopmpst_hwdata_i;
    else
       write_data = 32'd0;
end

always @(posedge iopmpst_clk_i or negedge iopmpst_rst_n_i ) begin
    if(!iopmpst_rst_n_i)
       addr_offset <= 3'd0;
    else if(en == 1'b1)
       addr_offset <= iopmpst_haddr_i[4:2];
    else
       addr_offset <= addr_offset;
end



// config iopmp id(main addr)
assign config_reg_wen = (write_data_en == 1'b1) && (addr_offset == CONFIG_REG_OFFSET);
always @(posedge iopmpst_clk_i or negedge iopmpst_rst_n_i ) begin
    if(!iopmpst_rst_n_i)
       config_reg <= 32'd0;
    else if(config_reg_wen == 1'b1)
       config_reg <= write_data;
    else
       config_reg <= config_reg;
end

//read enable
always@(*)begin
    if(read_data_en == 1 )begin
        case (addr_offset)
            CONFIG_REG_OFFSET       : iopmpst_hrdata_o = config_reg        ;
            SIOPMP_RULE_REG_OFFSET  : iopmpst_hrdata_o = iopmpst_readdata_i;
            // SIOPMP_SID_REG_OFFSET   : iopmpst_hrdata_o = iopmpst_readdata_i;
            DIOPMP_BEGIN_REG_OFFSET : iopmpst_hrdata_o = iopmpst_readdata_i;
            DIOPMP_SIZE_REG_OFFSET  : iopmpst_hrdata_o = iopmpst_readdata_i;
            DIOPMP_RULE_REG_OFFSET  : iopmpst_hrdata_o = iopmpst_readdata_i;
            DIOPMP_MODE_REG_OFFSET  : iopmpst_hrdata_o = iopmpst_readdata_i;
            SDIOPMP_ERROR_OFFSET    : iopmpst_hrdata_o = iopmpst_readdata_i;
            default                 : iopmpst_hrdata_o = 'd0         ;
        endcase
    end
    else begin
        iopmpst_hrdata_o      = 'd0;
    end
end 


assign hready           = 1'b1;

assign iopmpst_hready_o = hready;
assign iopmpst_hresp_o  = 2'b00;


assign iopmp_node_id       = config_reg[4:0];
assign iopmpst_cfg_en      = config_reg[5];
assign iopmpst_node_addr   = {iopmp_node_id,addr_offset};
assign iopmpst_node_wen_o  = (write_data_en == 1'b1);
assign iopmpst_node_ren_o  = (read_data_en == 1'b1);
assign iopmpst_node_wdata_o=  write_data;
assign iopmpst_intr_o = 1'b0;
assign iopmpst_node_clk_o  = iopmpst_clk_i;
assign iopmpst_node_rstn_o = iopmpst_rst_n_i;
endmodule
